feat(cli,api): difyctl version probes server and reports compat verdict#36356
Merged
Conversation
Contributor
Pyrefly Type Coverage
|
9976c37 to
a0ab9d5
Compare
GareArc
reviewed
May 19, 2026
Contributor
GareArc
left a comment
There was a problem hiding this comment.
runVersionProbe() only fires when the user explicitly runs difyctl version. Consider adding a lightweight staleness-based background check that surfaces compat warnings on any command. (e.g. on each command run, if last update time is older than 24h, fire /_version and update it.)
84b4e31 to
8876356
Compare
b243a2f to
4c36baa
Compare
Adds GET /openapi/v1/_version (no auth, mirrors _health) and reshapes difyctl version into a kubectl-style three-block report (client / server / compat) with -o text|json|yaml, --client to skip the probe, --short for scripting, and --check-compat to exit 64 when status != compatible. resolveBuildInfo now also falls back to package.json#difyctl.compat so pnpm dev/build/test all carry a real compat range without needing the release pipeline env vars. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every command that goes through buildAuthedContext (get / describe / run / auth-devices / ...) now silently refreshes a per-host compat snapshot at most every 24 h and shows one stderr line if the cached verdict is 'unsupported'. Banner is suppressed in pipeline mode (-o json|yaml|name), when stdout is not a TTY, on first-ever invocation against a host, and for 24 h after the last warning. No new env vars, no new flags, no new config keys — purely behavior driven by what the user is already doing. The existing 'difyctl version' command (explicit probe) is unaffected; it does not go through authedCtx and keeps its own contract intact. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aligns with the post-langgenius#36380 architecture: openapi-namespace types now live in packages/contracts/generated/api/openapi/, generated alongside console/service/web from the same Swagger spec. CLI imports rewrite to '@dify/contracts/api/openapi/types.gen', the local cli/src/types/data-contracts.ts is removed, and the orphan 'sync-models' npm script (its .sh file was already deleted upstream) is dropped. Behavioral: ServerVersionResponse + every other openapi type is now sourced from the shared package. Zero runtime change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ghter probe contract Address review comments on the version + auto-nudge feature: cache: - Replace `compat-snapshot` (verdict + server-version cache) with `nudge-store` which only persists `lastWarnedAt` per host. The nudge probe is now realtime; the cache only throttles how often we warn. Removes the lost-update / clock- skew / stale-warn classes of bugs in one stroke, net −408 LoC. - `markWarned` re-reads disk inside the write cycle so concurrent CLI invocations on different hosts can't clobber each other's timestamps. nudge: - Throttle-first decision tree: cheap checks (TTY, output format, silence window) run before any I/O. Probe failures are silently swallowed and never drive a warning. - `clientVersion` is now injected via `NudgeDeps` instead of reached through the `versionInfo` global, keeping the banner pure. probe / meta client: - Drop `bearer` from `MetaProbe` signature. `/openapi/v1/_version` is intentionally unauth (mirrors `_health`); the bearer parameter was dead code. - Move `META_PROBE_TIMEOUT_MS` from inside `MetaClient.serverVersion()` to the `createClient(...)` call at both probe sites (version command and per-command nudge). Both now also disable retry, so the default 30s × 3 budget never leaks into version probes. compat: - Clamp malformed `serverVersion` strings to 80 chars before quoting them in the verdict detail, so a hostile server response can't flood stderr. version command: - Emit the formatted report on stdout *before* exiting on `--check-compat`. This makes `difyctl version -o json --check-compat | jq ...` work the same way on the failure path as on success — pipelines get the JSON envelope, stderr gets the one-line reason, exit code signals the verdict.
4c36baa to
798d007
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GET /openapi/v1/_versionmeta endpoint (unauth, mirrors_health) exposes server{ version, edition }.difyctl versionbecomes a kubectl-style three-block report (client/server/compat). Flags:-o text|json|yaml,--clientto skip the probe,--shortfor scripting,--check-compatexits64when status ≠compatible(stdout still gets the full envelope first so pipelines work)./_versionin the background and, on the firstunsupportedverdict within 24h, prints one yellow stderr line nudging the user to rundifyctl version. Suppressed when stdout isn't a TTY or format isjson|yaml|name. Cachecache/nudge.jsonstores onlylastWarnedAt(no verdict caching = no stale-warn ambiguity); writes are atomic temp+rename with merge-on-write for concurrent CLIs. Probe failures are silenced — nudge never affects the host command.@dify/contracts/api/openapi/types.genpackage, completing the migration started in chore: switched to shared contract #36380.Probe degrades gracefully — unreachable / unauth / non-semver / empty →
compat.status = unknown, default exit0. Only--check-compattreatsunsupportedandunknownas failures.Closed three-block envelope (top-level keys stable, additive within blocks):
{ "client": { "version", "commit", "buildDate", "channel", "platform", "arch" }, "server": { "endpoint", "reachable", "version?", "edition?" }, "compat": { "minDify", "maxDify", "status", "detail" } }Two single sources of truth:
cli/package.json#difyctl.compat(the tested Dify range) andapi/pyproject.toml#[project].version(the actual Dify version, also driving docker tags / PyPI).770/770 tests green (CLI + API).
Screenshots
CLI change — no UI; sample output:
difyctl versionagainst an unsupported serverAuthed command with auto-nudge banner (first time within 24h)
Banner appears once per host per 24h on stderr; throttled re-runs are silent.
Checklist
make lint && make type-check(backend) andcd web && pnpm exec vp staged(frontend) to appease the lint gods